﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Media.Imaging;
using System.Windows.Controls;
using System.Diagnostics;
using System.Security.Cryptography;
using System.IO;
using System.Xml;
using System.Collections.ObjectModel;
using System.Threading;
using BBEverNote.Commons;
using BBEverNote.Commands;
using BBEverNote.Models;
using Thrift;
using Thrift.Protocol;
using Thrift.Server;
using Thrift.Transport;
using Evernote.EDAM.Type;
using Evernote.EDAM.UserStore;
using Evernote.EDAM.NoteStore;
using Evernote.EDAM.Error;
using EvernoteSharp;
using HtmlAgilityPack;
using System.Windows.Media;
using System.Text.RegularExpressions;

namespace BBEverNote.ViewModels
{
    public class MainViewModel : ViewModelBase
    {
        public MainViewModel()
        {
            // Blank
            LoadSetting();
        }

        private string _resourceRegex = @"(?is)(<en-media(?=.*?type=\""image/(?:png|jpeg|gif))(?=.*?hash=\""[^\""]{31})[^>]+>)";
        private string _resourceHashReplaceRegex = @"(hash=\""[A-Za-z0-9]{32}\"")";
        private string _filepath = "BBEverNote.info";
        private string _infoseparator = "; ";
        public delegate void MethodInvoker();

        #region Attributes

        private bool _isBusy = false;

        public bool IsBusy
        {
            get { return _isBusy; }
            set
            {
                _isBusy = value;
                OnPropertyChanged("IsBusy");
            }
        }

        private string _busyContent = "就绪";

        public string BusyContent
        {
            get { return _busyContent; }
            set
            {
                _busyContent = value;
                OnPropertyChanged("BusyContent");
            }
        }

        private bool _isOnlySaveTxt = true;

        public bool IsOnlySaveTxt
        {
            get { return _isOnlySaveTxt; }
            set 
            { 
                _isOnlySaveTxt = value;
                OnPropertyChanged("IsOnlySaveTxt");
            }
        }

        private string _saveDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

        public string SaveDirectory
        {
            get { return _saveDirectory; }
            set
            {
                _saveDirectory = value;
                OnPropertyChanged("SaveDirectory");
            }
        }

        private ObservableCollection<ENNoteBookModel> _enNoteBooks = new ObservableCollection<ENNoteBookModel>();

        public ObservableCollection<ENNoteBookModel> ENNoteBooks
        {
            get { return _enNoteBooks; }
            set
            {
                _enNoteBooks = value;
                OnPropertyChanged("ENNoteBooks");
            }
        }


        private ObservableCollection<Notebook> _noteBooks = new ObservableCollection<Notebook>();

        public ObservableCollection<Notebook> NoteBooks
        {
            get { return _noteBooks; }
            set
            {
                _noteBooks = value;
                OnPropertyChanged("NoteBooks");
            }
        }


        private EvernoteModel _enModel = new EvernoteModel();

        public EvernoteModel ENModel
        {
            get { return _enModel; }
            set
            {
                _enModel = value;
                OnPropertyChanged("ENModel");
            }
        }
        #endregion

        #region Command
        private DelegateCommand exitCommand;
        public DelegateCommand ExitCommand
        {
            get
            {
                if (exitCommand == null)
                {
                    exitCommand = new DelegateCommand(Exit);
                }
                return exitCommand;
            }
        }

        private void Exit()
        {
            SaveSetting();

            Application.Current.Shutdown();
        }

        private DelegateCommand<object> linkCommand;

        public DelegateCommand<object> LinkCommand
        {
            get
            {
                if (linkCommand == null)
                {
                    linkCommand = new DelegateCommand<object>(OpenLink);
                }
                return linkCommand;
            }
            set { linkCommand = value; }
        }

        private void OpenLink(object param)
        {
            string link = param.ToString();
            Process.Start(link);
        }

        private DelegateCommand<object> logOnCommand;
        public DelegateCommand<object> LogOnCommand
        {
            get
            {
                if (logOnCommand == null)
                {
                    logOnCommand = new DelegateCommand<object>(LogOnPro);
                }
                return logOnCommand;
            }
        }

        private void LogOnPro(object param)
        {
            LogOn();
        }

        private DelegateCommand browserFolderCommand;

        public DelegateCommand BrowserFolderCommand
        {
            get
            {
                if (browserFolderCommand == null)
                {
                    browserFolderCommand = new DelegateCommand(BrowserFolder);
                }
                return browserFolderCommand;
            }
        }

        private void BrowserFolder()
        {
            System.Windows.Forms.FolderBrowserDialog dlg = new System.Windows.Forms.FolderBrowserDialog();
            dlg.SelectedPath = SaveDirectory;
            dlg.Description = "请选择电子书保存的位置。";

            System.Windows.Forms.DialogResult result = dlg.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                SaveDirectory = dlg.SelectedPath;
            }
            else
            {

            }
        }

        private DelegateCommand<object> createSNBCommand;

        public DelegateCommand<object> CreateSNBCommand
        {
            get
            {
                if (createSNBCommand == null)
                {
                    createSNBCommand = new DelegateCommand<object>(threadpool_CreateSNB);
                }
                return createSNBCommand;
            }
        }

        #endregion

        #region Function

        public bool LogOn()
        {
            IsBusy = true;
            BusyContent = "正在登录。。。";

            StoreFactory sf = new StoreFactory(new Uri(ENModel.EdamBaseUrl/*"https://www.evernote.com/"*/), ENModel.ConsumerKey, ENModel.ConsumerSecret);
            ENModel.EnUserStoreW = sf.CreateUserStore();

            try
            {
                if (!ENModel.EnUserStoreW.CheckVersion())
                {
                    IsBusy = false;
                    return false;
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.Message);
                MessageBox.Show("操作超时。。。");
                IsBusy = false;
                return false;
            }

            if (ENModel.UserName.Length <= 0)
            {
                MessageBox.Show("用户名不能为空， 请重新输入");
                IsBusy = false;
                return false;
            }

            if (ENModel.Password.Length <= 0)
            {
                MessageBox.Show("密码不能为空， 请重新输入");
                IsBusy = false;
                return false;
            }

            BusyContent = "正在验证。。。";

            AuthenticationResult authResult = null;
            try
            {
                authResult = ENModel.EnUserStoreW.Authenticate(ENModel.UserName, ENModel.Password);
            }
            catch (EDAMUserException ex)
            {
                String parameter = ex.Parameter;
                EDAMErrorCode errorCode = ex.ErrorCode;

                Console.WriteLine("Authentication failed (parameter: " + parameter + " errorCode: " + errorCode + ")");

#if DEBUG
                if (errorCode == EDAMErrorCode.INVALID_AUTH)
                {
                    switch (parameter)
                    {
                        case "consumerKey":
                            if (ENModel.ConsumerKey == "en-edamtest")
                            {
                                Console.WriteLine("You must replace the variables consumerKey and consumerSecret with the values you received from Evernote.");
                            }
                            else
                            {
                                Console.WriteLine("Your consumer key was not accepted by " + ENModel.EvernoteHost);
                            }
                            Console.WriteLine("If you do not have an API Key from Evernote, you can request one from http://www.evernote.com/about/developer/api");

                            break;
                        case "username":
                            Console.WriteLine("You must authenticate using a username and password from " + ENModel.EvernoteHost);
                            if (ENModel.EvernoteHost == "www.evernote.com" == false)
                            {
                                Console.WriteLine("Note that your production Evernote account will not work on " + ENModel.EvernoteHost + ",");
                                Console.WriteLine("you must register for a separate test account at https://" + ENModel.EvernoteHost + "/Registration.action");
                            }
                            break;
                        case "password":
                            Console.WriteLine("The password that you entered is incorrect");
                            break;
                        default:
                            break;
                    }
                }
#endif

                MessageBox.Show("用户名密码错误， 请重新输入");
                IsBusy = false;
                return false;
            }

            ENModel.ENUser = authResult.User;
            ENModel.AuthToken = authResult.AuthenticationToken;

            ENModel.EnNoteStoreW = sf.CreateNoteStore();
            var defaultNotebook = ENModel.EnNoteStoreW.GetDefaultNotebook();

//             BusyContent = "正在获取列表。。。";
//             GetAllNoteBooks();
            ThreadPool.QueueUserWorkItem(new WaitCallback(GetAllNoteBooks));

            BusyContent = "就绪";
            IsBusy = false;
            return true;
        }

        public void LoadSetting()
        {
            try
            {
                FileStream fs = File.Open(_filepath, FileMode.Open);

                byte[] result = new byte[1024];
                UTF8Encoding temp = new UTF8Encoding(true);

                int len = fs.Read(result, 0, result.Length);

                string info = AES.AESDecrypt(temp.GetString(result, 0, len));
                string[] str = info.Split(_infoseparator.ToCharArray());

                ENModel.UserName = str[0];
                ENModel.Password = str[2];
                ENModel.IsRememberMe = bool.Parse(str[4]);
                ENModel.IsAutoLogOn = bool.Parse(str[6]);
                SaveDirectory = str[8];

                if (!ENModel.IsRememberMe)
                {
                    ENModel.Password = "";
                }
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }

        public void SaveSetting()
        {
            try
            {
                FileStream fs = File.Open(_filepath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);

                if (!ENModel.IsRememberMe)
                {
                    ENModel.Password = " ";
                }

                string info = string.Format("{0:s}{1:s}{2:s}{1:s}{3:s}{1:s}{4:s}{1:s}{5:s}",
                    ENModel.UserName,
                    _infoseparator,
                    ENModel.Password,
                    ENModel.IsRememberMe.ToString(),
                    ENModel.IsAutoLogOn.ToString(),
                    SaveDirectory);

                Byte[] temp = new UTF8Encoding(true).GetBytes(AES.AESEncrypt(info));

                fs.Write(temp, 0, temp.Length);
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

        }

        private void threadpool_CreateSNB(object param)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(CreateSNB), param);
        }

        private void CreateSNB(object param)
        {
            Button btn = param as Button;

            if (App.Current.Dispatcher != null)
            {
                System.Windows.Threading.Dispatcher.FromThread(App.Current.Dispatcher.Thread).Invoke((MainViewModel.MethodInvoker)delegate
                {
                    btn.IsEnabled = false;
                }
                , null);
            }
            else
            {
                btn.IsEnabled = false;
            }

            IsBusy = true;
            BusyContent = "开始转换...";

            ConverterToSNB();

            BusyContent = "就绪";
            IsBusy = false;

            if (App.Current.Dispatcher != null)
            {
                System.Windows.Threading.Dispatcher.FromThread(App.Current.Dispatcher.Thread).Invoke((MainViewModel.MethodInvoker)delegate
                {
                    btn.IsEnabled = true;
                }
                , null);
            }
            else
            {
                btn.IsEnabled = true;
            }
        }

        private void ConverterToSNB()
        {
            foreach (ENNoteBookModel enNoteBook in ENNoteBooks)
            {
                if (IsCheckValid(enNoteBook.IsChecked))
                {
                    DownloadSingleBook(enNoteBook);

                    BusyContent = "正在生成 " + enNoteBook.Name + " 电子书...";
                    CreateSNBFromNotes(enNoteBook);

                    DeleteTempFiles(enNoteBook);
                }
            }
        }

        private void DownloadSingleBook(ENNoteBookModel enNoteBook)
        {
            CreateSNBDirectory(enNoteBook.Name);

            CreateBookSNBF(enNoteBook);

            CreateTocSNBF(enNoteBook);

            NoteFilter filter = new NoteFilter();
            filter.NotebookGuid = enNoteBook.EnGuid;

            NoteCollectionCounts counts = ENModel.EnNoteStoreW.FindNoteCounts(filter);
            if (counts.NotebookCounts != null)
            {
                NoteList noteList = ENModel.EnNoteStoreW.FindNotes(filter, 0, counts.NotebookCounts[enNoteBook.EnGuid]);

                foreach (Note note in noteList.Notes)
                {
                    var values = from n in enNoteBook.SubNoteBooks
                                 where (note.Title == n.Name) && IsCheckValid(n.IsChecked)
                                 select n;

                    if (values.ToList().Count <= 0)
                    {
                        continue;
                    }

                    BusyContent = "正在下载" + note.Title + "...";

                    //
                    try
                    {
                        string content = ENModel.EnNoteStoreW.GetNoteContent(note.Guid);
                        File.WriteAllText(enNoteBook.Name + "\\" + note.Guid + ".html", content);
                    }
                    catch (System.Exception ex)
                    {
                        Debug.WriteLine(ex.Message);
                    }

                    if (note.Resources == null)
                    {
                        continue;
                    }

                    if (IsOnlySaveTxt)
                    {
                        continue;
                    }

                    foreach (Resource rc in note.Resources)
                    {
                        try
                        {
                            string[] test = rc.Mime.Split("/".ToCharArray());

                            if (test[0].Equals("image", StringComparison.CurrentCultureIgnoreCase))
                            {
                                byte[] data = ENModel.EnNoteStoreW.GetResourceData(rc.Guid);

                                string path = enNoteBook.Name + "\\snbc\\images\\";
                                Directory.CreateDirectory(path);

                                //                                 Stream stream = new MemoryStream(data);
                                //                                 System.Drawing.Image img = System.Drawing.Image.FromStream(stream);
                                //                                 MakeGrayscale3(ref img);
                                //                                 img.Save(path + "\\" + note.Guid + "+"+ note.Guid + "+" + rc.Guid + @"." + test[1]);

                                File.WriteAllBytes(path + "\\" + note.Guid + "+" + BitConverter.ToString(rc.Data.BodyHash).Replace("-", "") + @"." + test[1], data);
                            }
                        }
                        catch (System.Exception ex)
                        {
                            Debug.WriteLine(ex.Message);
                        }
                    }
                }
            }
        }

        private void CreateSNBFromNotes(ENNoteBookModel enNoteBook)
        {
            string path = enNoteBook.Name;
            if (Directory.Exists(path))
            {
                DirectoryInfo dir = new DirectoryInfo(path);

                int i = 0;
                foreach (FileInfo f in dir.GetFiles("*.html"))
                {
                    i++;
                    string snbc = string.Format("{0:s}.snbc", f.Name.Replace(f.Extension, ""));

                    FileStream file2 = new FileStream(path + "\\snbc\\" + snbc, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None);
                    StreamWriter writer = new StreamWriter(file2, Encoding.GetEncoding("UTF-8"));

                    string textBegin = string.Format("<?xml version='1.0' encoding='UTF-8'?><snbc><head><title><![CDATA[{0:s}]]></title></head><body>", enNoteBook.Name);
                    string textEnd = "</body></snbc>";
                    string body = "";

                    FileStream file = new FileStream(f.DirectoryName + "\\" + f.Name, FileMode.Open);
                    StreamReader reader = new StreamReader(file, Encoding.GetEncoding("UTF-8"));
                    string line = reader.ReadToEnd();

                    if (IsOnlySaveTxt)
                    {
                        //{Text
                        HtmlToText htt = new HtmlToText();
                        string content = htt.StripHTML(line);

                        if (content.Length > 0)
                        {
                            body += @"<text><![CDATA[";
                            body += content;
                            body += @"]]></text>";
                        }
                        //Text} 
                    }
                    else
                    {
                        //Process html to text & resource.
                        List<string> substrings = Regex.Split(line, _resourceRegex).ToList();
                        Regex reg = new Regex(_resourceRegex);
                        foreach (string match in substrings)
                        {
                            if (!reg.IsMatch(match))
                            {
                                //{Text
                                HtmlToText htt = new HtmlToText();
                                string content = htt.StripHTML(match);

                                if (content.Length > 0)
                                {
                                    body += @"<text><![CDATA[";
                                    body += content;
                                    body += @"]]></text>";
                                }
                                //Text} 
                            }
                            else
                            {
                                DirectoryInfo dirImg = new DirectoryInfo(path + "\\snbc\\images\\" /*+ f.Name.Replace(f.Extension, "")*/);
                                try
                                {
                                    string hashcode = Regex.Match(match, _resourceHashReplaceRegex).Groups[1].Value;
                                    hashcode = hashcode.Replace("hash=\"", "");
                                    hashcode = hashcode.Replace("\"", "");

                                    foreach (FileInfo fImg in dirImg.GetFiles("*" + hashcode + ".*"))
                                    {
                                        if (fImg.Name.IndexOf(f.Name.Replace(f.Extension, "")) != -1)
                                        {
                                            body += @"<img>";
                                            body += (fImg.Name);
                                            body += @"</img>";
                                        }
                                    }
                                }
                                catch (System.Exception ex)
                                {
                                    Debug.WriteLine(ex.Message);
                                }
                            }
                        }
                    }
                    
                    writer.WriteLine(textBegin + body + textEnd);

                    writer.Close();
                    file2.Close();
                }

                BambookCore.BambookPackSnbFromDir(SaveDirectory + "\\" + enNoteBook.Name + ".snb", enNoteBook.Name);
            }
        }

        private void CreateSNBDirectory(string name)
        {
            string appPath = System.IO.Directory.GetCurrentDirectory();
            string path = appPath + "\\" + name;

            try
            {
                Directory.CreateDirectory(path);

                Directory.CreateDirectory(path + "\\snbc");
                Directory.CreateDirectory(path + "\\snbc\\images");
                Directory.CreateDirectory(path + "\\snbf");
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        private void CreateBookSNBF(ENNoteBookModel enNoteBook)
        {
            string str = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ".book.snbf";
            System.IO.Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(str);

            XmlDocument doc = new XmlDocument();
            doc.Load(stream);

            string path = @"/book-snbf[1]/head[1]/name[1]";
            var name = doc.DocumentElement.SelectSingleNode(path);
            name.InnerText = enNoteBook.Name;

            path = @"/book-snbf[1]/head[1]/author[1]";
            var author = doc.DocumentElement.SelectSingleNode(path);
            author.InnerText = ENModel.UserName;

            path = @"/book-snbf[1]/head[1]/generator[1]";
            var generator = doc.DocumentElement.SelectSingleNode(path);
            generator.InnerText = "EverNote 锦书生成器 V1.01";

            path = @"/book-snbf[1]/head[1]/created[1]";
            var created = doc.DocumentElement.SelectSingleNode(path);
            created.InnerText = DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString();

            doc.Save(enNoteBook.Name + "\\snbf\\book.snbf");
        }

        private void CreateTocSNBF(ENNoteBookModel enNoteBook)
        {
            string str = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name + ".toc.snbf";
            System.IO.Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(str);

            XmlDocument doc = new XmlDocument();
            doc.Load(stream);

            string path = @"/toc-snbf[1]/head[1]/chapters[1]";
            var count = doc.DocumentElement.SelectSingleNode(path);

            path = @"/toc-snbf[1]/body[1]";
            var body = doc.DocumentElement.SelectSingleNode(path);

            var values = from n in enNoteBook.SubNoteBooks
                         where IsCheckValid(n.IsChecked)
                         select n;

            count.InnerText = values.Count().ToString();

            foreach (var e in values)
            {
                string snbc = string.Format("{0:s}.snbc", e.EnGuid);

                XmlElement elem = doc.CreateElement("chapter");

                XmlAttribute attr = doc.CreateAttribute("src");
                attr.Value = snbc;

                XmlCDataSection CData;
                CData = doc.CreateCDataSection(e.Name);

                elem.SetAttributeNode(attr);
                elem.AppendChild(CData);

                body.AppendChild(elem);
            }

            doc.Save(enNoteBook.Name + "\\snbf\\toc.snbf");
        }

        private void DeleteTempFiles(ENNoteBookModel enNoteBook)
        {
            DirectoryInfo dir = new DirectoryInfo(enNoteBook.Name);

            try
            {
                Directory.Delete(enNoteBook.Name, true);
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }

        private bool IsCheckValid(bool? isCheck)
        {
            if ((isCheck.HasValue && isCheck.Value)
                || !isCheck.HasValue)
            {
                return true;
            }

            return false;
        }

        private void GetAllNoteBooks(object param)
        {
            IsBusy = true;
            BusyContent = "正在获取列表。。。";

            List<Notebook> notebooks = ENModel.EnNoteStoreW.ListNotebooks().ToList();
            NoteBooks.Clear();

            foreach (Notebook notebook in notebooks)
            {
                ENNoteBookModel item = new ENNoteBookModel();
                item.Name = notebook.Name;
                item.IsChecked = false;
                item.EnGuid = notebook.Guid;

                NoteFilter filter = new NoteFilter();
                filter.NotebookGuid = notebook.Guid;

                try
                {
                    NoteCollectionCounts counts = ENModel.EnNoteStoreW.FindNoteCounts(filter);

                    if (counts.NotebookCounts != null)
                    {
                        NoteList noteList = ENModel.EnNoteStoreW.FindNotes(filter, 0, counts.NotebookCounts[notebook.Guid]);

                        foreach (Note note in noteList.Notes)
                        {
                            ENNoteBookModel child = new ENNoteBookModel();

                            child.ParentNote = item;
                            child.Name = note.Title;
                            child.IsChecked = false;
                            child.EnGuid = note.Guid;

                            item.SubNoteBooks.Add(child);
                        }
                    }
                }
                catch (System.Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }

                if (App.Current.Dispatcher != null)
                {
                    System.Windows.Threading.Dispatcher.FromThread(App.Current.Dispatcher.Thread).Invoke((MethodInvoker)delegate
                    {
                        ENNoteBooks.Add(item);
                        NoteBooks.Add(notebook);
                    }
                    , null);
                }
                else
                {
                    ENNoteBooks.Add(item);
                    NoteBooks.Add(notebook);
                }
            }

            BusyContent = "就绪";
            IsBusy = false;
        }

        private void MakeGrayscale3(ref System.Drawing.Image original)
        {
            //get a graphics object from the new image
            Graphics g = Graphics.FromImage(original);

            //create the grayscale ColorMatrix
            ColorMatrix colorMatrix = new ColorMatrix(
            new float[][] 
            {
             new float[] {.3f, .3f, .3f, 0, 0},
             new float[] {.59f, .59f, .59f, 0, 0},
             new float[] {.11f, .11f, .11f, 0, 0},
             new float[] {0, 0, 0, 1, 0},
             new float[] {0, 0, 0, 0, 1}
             });

            //create some image attributes
            ImageAttributes attributes = new ImageAttributes();

            //set the color matrix attribute
            attributes.SetColorMatrix(colorMatrix);

            //draw the original image on the new image
            //using the grayscale color matrix
            g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height),
            0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes);

            //dispose the Graphics object
            g.Dispose();
        }

        #endregion

    }
}
